"""
Copyright (c) 2022 Huawei Technologies Co.,Ltd.

openGauss is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:

          http://license.coscl.org.cn/MulanPSL2

THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
"""
"""
Case Type   : 表文件
Case Name   : 表文件损坏
Create At   : 2020/07/01
Owner       : @wan005
Description :
    1.创建表，并插入一部分数据
    2.等待回放完成和主备同步
    3.查找表文件名称打开表数据文件，删除一部分内容
    4.向表中插入数据
    5.重启数据库，恢复集群状态
    6.向表中插入数据
Expect      :
    1.创建表，并插入一部分数据成功
    2.等待回放完成和主备同步成功
    2.查找表文件名称打开表数据文件，删除一部分内容，删除成功
    3.向表中插入数据失败
    4.重启数据库，恢复集群状态成功
    5.表文件恢复，向表中插入数据成功
History     :
    Modified by @peilinqian：表文件损坏后，可以插入数据成功，与预期不符，增加必要查询信息以方便定位问题；
    # Modified by @wan005：增加等待脏页刷新和主备一致检查
    Modified by @wan005 at 2022.02.18：优化期望结果
    Modified by @peilinqian at 2022.04.07：表文件损坏后插入数据概率性成功，增加vacuum操作使稳定panic
        增加过程日志打印、和panic概率重启失败场景下恢复集群状态操作
    Modified by @peilinqian at 2022.04.20：表文件损坏插入panic，需要增加等待从表文件读取数据后才能出现
        暂时先修改用例，已反馈开发定位分析具体业务逻辑
"""

import os
import time
import unittest

from testcase.utils.Common import Common
from testcase.utils.CommonSH import CommonSH
from testcase.utils.CommonSH import RestartDbCluster
from testcase.utils.Constant import Constant
from testcase.utils.Logger import Logger
from yat.test import Node
from yat.test import macro


class RenameTablefile(unittest.TestCase):
    nodes_tuple = ('PrimaryDbUser', 'Standby1DbUser', 'Standby2DbUser')

    @RestartDbCluster(*nodes_tuple)
    def setUp(self):
        self.log = Logger()
        self.log.info(f'----{os.path.basename(__file__)} start----')
        self.Constant = Constant()
        self.dbUserNode = Node(node='PrimaryDbUser')
        self.com = Common()
        self.t_name = 'reliability_dbsys062'
        self.pri_sh = CommonSH('PrimaryDbUser')
        self.pri_root = Node(node='PrimaryRoot')
        self.sta_1_sh = CommonSH('Standby1DbUser')

    def test_rename_tablefile(self):
        step_txt = '----step1:创建表，并写入数据; expect:创建成功----'
        self.log.info(step_txt)
        create_sql = f'drop table if exists {self.t_name}; ' \
            f'create table {self.t_name}(id int); ' \
            f'insert into {self.t_name} values(generate_series(1,10000));'
        create_result = self.pri_sh.executDbSql(create_sql)
        self.log.info(create_result)
        self.assertIn('INSERT', create_result, '执行失败:' + step_txt)

        step_txt = '----step2:主机刷盘和等待主备同步; expect:成功----'
        self.log.info(step_txt)
        select_cmd = f'checkpoint;'
        select_result = self.pri_sh.executDbSql(select_cmd)
        self.log.info(select_result)
        result = self.pri_sh.check_data_consistency()
        self.assertTrue(result, '执行失败:' + step_txt)

        step_txt = '----step3:查找表文件名称打开表数据文件，删除一部分内容; expect:成功----'
        self.log.info(step_txt)
        self.log.info('----查询oid----')
        oid_sql = f"select oid from pg_class where relname='{self.t_name}';"
        self.log.info(oid_sql)
        oid_result = self.pri_sh.executDbSql(oid_sql)
        self.log.info(oid_result)
        oid = oid_result.splitlines()[-2].strip()
        self.log.info(oid)

        self.log.info('----通过oid查询文件位置----')
        file_cmd = f"select pg_relation_filepath({oid}::regclass);"
        self.log.info(file_cmd)
        file_path = self.pri_sh.executDbSql(file_cmd).splitlines()[-2].strip()
        self.log.info(file_path)
        table_file = os.path.join(macro.DB_INSTANCE_PATH, file_path)

        self.log.info('----删除一部分内容----')
        ls_cmd = f'ls -al {table_file}'
        ls_table_file = self.dbUserNode.sh(ls_cmd).result()
        self.log.info(ls_table_file)
        del_content_cmd = f"sed -i '1,1d' {table_file} "
        self.log.info(del_content_cmd)
        msg = self.dbUserNode.sh(del_content_cmd).result()
        self.log.info(msg)
        ls_table_file = self.dbUserNode.sh(ls_cmd).result()
        self.log.info(ls_table_file)

        step_txt = '----step4:向表中插入数据; expect:失败----'
        self.log.info(step_txt)
        self.log.info('----等待从表文件读取数据----')
        for i in range(20):
            select_cmd = f'select count(*) from {self.t_name};'
            select_result = self.pri_sh.executDbSql(select_cmd)
            self.log.info(select_result)
            time.sleep(1)
            if '9944' in select_result:
                break

        self.log.info('----插入数据----')
        insert_cmd = f'insert into {self.t_name} values' \
            f'(generate_series(10001,30000));'
        self.log.info(insert_cmd)
        result = self.pri_sh.executDbSql(insert_cmd)
        self.log.info(result)
        select_cmd = f'select count(*) from {self.t_name};'
        select_result = self.pri_sh.executDbSql(select_cmd)
        self.log.info(select_result)
        self.assertIn('PANIC', result)
        self.assertIn(self.Constant.CONNECT_LOST_MSG, result,
                      '执行失败:' + step_txt)

        self.log.info('----查询主备状态----')
        status = self.pri_sh.getDbClusterStatus('status')
        self.assertFalse(status)

        step_txt = '----step5:重启数据库，恢复集群状态; expect:成功----'
        self.log.info(step_txt)
        is_stopped = self.pri_sh.stopDbCluster()
        self.assertTrue(is_stopped)
        is_started = self.pri_sh.startDbCluster()
        if not is_started:
            self.log.info('----重启数据库失败后，恢复集群----')
            success_msg = self.Constant.FAILOVER_SUCCESS_MSG
            self.log.info('--备机进行failover--')
            result = self.sta_1_sh.executeGsctl('failover', success_msg)
            self.assertTrue(result, '执行失败:' + step_txt)
            result = self.pri_sh.exec_refresh_conf()
            self.assertTrue(result, '执行失败:' + step_txt)
            self.log.info('--重建原主--')
            self.pri_sh.executeGsctl('build',
                                     self.Constant.REBUILD_SUCCESS_MSG)
            self.log.info('--进行主备切换--')
            success_msg = self.Constant.SWITCHOVER_SUCCESS_MSG
            result = self.pri_sh.executeGsctl('switchover', success_msg)
            self.assertTrue(result, '执行失败:' + step_txt)
            result = self.pri_sh.exec_refresh_conf()
            self.assertTrue(result, '执行失败:' + step_txt)

        self.log.info('----查询主备状态----')
        status = self.pri_sh.getDbClusterStatus('status')
        self.assertTrue(status, '执行失败:' + step_txt)

        self.log.info('----查询表数据文件----')
        ls_table_file = self.dbUserNode.sh(ls_cmd).result()
        self.log.info(ls_table_file)

        step_txt = '----step6:向表中插入数据; expect:成功----'
        self.log.info(step_txt)
        self.log.info('----插入数据----')
        insert_cmd = f'insert into {self.t_name} ' \
            f'values(generate_series(1,10000));'
        self.log.info(insert_cmd)
        result = self.pri_sh.executDbSql(insert_cmd)
        self.log.info(result)
        self.assertIn('INSERT', result, '执行失败:' + step_txt)

        self.log.info('----查询表数据量----')
        select_cmd = f'select count(*) from {self.t_name};'
        self.log.info(select_cmd)
        result = self.pri_sh.executDbSql(select_cmd)
        self.log.info(result)
        self.assertTrue('19944' in result or '20000' in result,
                        '执行失败:' + step_txt)

    def tearDown(self):
        step_txt = '----drop table----'
        self.log.info(step_txt)
        sql = f'drop table if exists {self.t_name};'
        result = self.pri_sh.executDbSql(sql)
        self.log.info(result)
        self.log.info('----teardown断言----')
        self.assertIn(self.Constant.DROP_TABLE_SUCCESS, result,
                      '执行失败:' + step_txt)

        self.log.info(f'----{os.path.basename(__file__)} end----')
